/* 全世界最小中文自举编译器 */
/*
 * PE可执行文件头
 * ----------------------------------------
 * |        |         |DOS MZ头（64字节）     |64字节   <- IMAGE_DOS_HEADER
 * |        | DOS头    |--------------------|
 * |        |         |DOS Stub(空间大小不确定)|64字节    <- 通常是一个小的DOS程序，显示 "This program cannot be run in DOS mode"
 * |        |------------------------------|
 * |PE文件头部|         |  PE签名             |4字节     <- "PE\0\0" (0x50 0x45 0x00 0x00)
 * |        |         |--------------------|
 * |        |NT头(PE头)| COFF文件头          |20字节     <- IMAGE_FILE_HEADER
 * |        |         |--------------------|
 * |        |         |PE可选头(Standard part)|240字节    <- IMAGE_OPTIONAL_HEADER (Standard part)
 * |        |         |PE可选头(NT part)     |    <- IMAGE_OPTIONAL_HEADER (NT part)
 * |---------------------------------------|
 * |         节表（节的数量 X 40个字节）          |    <- IMAGE_SECTION_HEADER (数组，每个元素对应一个节，与 PE数据区 一一对应) .PE中所有节的目录
 * |    (NumberOfSections 决定了此表的条目数量) | 64位节表的开始偏移 = 64+64+4+20+240 = 392
 * |----------------------------------------|
 * |        |         | .text节（代码）       | 可执行代码节
 * |PE数据区  |         |--------------------|
 * |        | 节内容    | .rdata             | 只读数据节（如字符串常量、const） (.idata节 和 .edata 节通常镶嵌在.rdata内)
 * |        |         |--------------------|
 * |        |         |.data（数据节）       |  已初始化的可读写数据 。
 * |        |         |--------------------|
 * |        |         | .bss               | 未初始化或零初始化的数据
 * |        |         |--------------------|
 * |        |         | .xdata               | 异常处理元数据（Exception Handling metadata），用于 x64 和 ARM 的结构化异常处理（SEH）。
 * |        |         |--------------------|
 * |        |         | .pdata               | 函数展开信息（Function unwind info），配合 .xdata 使用，在 x64/ARM PE 中必需。
 * |        |         |--------------------|
 * |        |         | .reloc              | 重定位信息
 * |        |         |--------------------|
 * |        |         | .rsrc               | 资源（图标、对话框、版本信息等）
 * |        |         |--------------------|
 * |        |         | .CRT               | Microsoft C 运行时初始化数据（如全局构造函数指针），较老版本 MSVC 使用。
 * |        |         |--------------------|
 * |        |         | .loadcfg           | 加载配置目录（Load Configuration Directory）内容，包含安全特性如 SEH 处理器列表、CFG 信息等。
 * |        |         |--------------------|
 * |        |         | ...其他节            |
 * |----------------------------------------|
 * |  PE结束（特殊情况下可能还有其他信息）         |   <- 可能还有：1.签名信息；2.调试信息；3.附加资源或自定义数据；4.对齐填充
 * ----------------------------------------
结论（在标准情况下）：
32 位 PE 文件：节表起始偏移 = 0x178 = 376 字节
64 位 PE 文件：节表起始偏移 = 0x188 = 392 字节
注意：节的文件对齐，512字节(0x200)对齐

 FileAlignment	节在 PE 文件中的原始数据（raw data）必须按此值对齐（即 PointerToRawData 和 SizeOfRawData 需对齐）	通常为 512 字节（或 0x200），最小可为 1
 SectionAlignment	节加载到内存后，其虚拟地址（VirtualAddress）和大小（VirtualSize 对齐后的值）必须按此值对齐	通常为 4KB（0x1000），即页面大小
注意：FileAlignment 不能大于 SectionAlignment，且两者都必须是 2 的幂。

 * .bss : 未初始化的全局变量，静态变量的内存区域，初始化之后.bss节将会清零，属于静态内存分配，即程序一开始就将其清零了。.bss在应用程序的二进制映像文件中并不存在，不占用可执行文件的空间。
 * .data : 程序中已被初始化的全局变量，常量，静态变量的内存区域。也就是静态存储区。data段为数据分配空间，数据保存在目标文件中。
 * .txt : 代码
 * .rdata : 常量节（只读数据节）
 *
 * #include <stdio.h>
 * int value1;                //全局的未被初始化的变量，处于bss段
 * static void *value2 = NULL //全局的指针变量，处于bss段
 * int value3 = 5             //全局的被初始化的变量，处于data段
 * const int value4 = 5;      //加上const变为只读的常量，处于rodata段
 * main()
 * {
 *     return 0;
 * }
 * */


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <fcntl.h>
#include <stdint.h> /*uint8_t*/

uint8_t  *全_源码指针 ,
        *l_全_源码指针,
        *备份_全_源码指针;
char     *全_数据段,
        *起始_全_数据段,
        *全_操作码,
        *全_源文件名称;

int      *全_代码段,
        *起始_全_代码段,
        *l_全_代码段,
        *全_case语句修补,
        *全_break语句修补,
        *全_default语句修补,
        *全_数据类型大小,
        全_下个可用类型,
        全_单词编码,
        全_当前字符,
        全_单词的值,
        全_声明数据类型,
        全_局部变量偏移,
        全_行号,
        全_打印源码,
        全_打印指令;

int 标识符最大长度,源码缓冲区大小;

int *全_下条指令,*备份_全_下条指令, *全_栈顶 , *全_栈顶备份,i,j;

// 这个结构体主要用于编译器的符号表管理，跟踪源代码中所有标识符的信息，包括变量、函数、关键字等，并支持作用域管理和类型检查。
struct 单词结构 {
    int 单词编码;
    uint64_t 哈希值;
    char *字符串;
    int 声明类型;         //如全局变量、局部变量、函数等
    int 数据类型;       //如int、char、指针等
    int 地址;          //变量或函数在内存中的地址或偏移量
    int 表示的结构体名称;  //结构体标识符
    int 类别备份;     //在作用域处理中使用
    int 数据类型备份;  //在作用域处理中使用
    int 地址备份;   //在作用域处理中使用
    int 是否已定义;      // 函数是否已定义 (0: 仅声明, 1: 已定义)
    int 参数数量;     // 参数数量
} *全_当前单词,  // currently parsed identifier
*全_单词表, // 符号表指针，存储所有已解析的标识符信息
*单词main,
        *单词开始;

//单词结构
struct 结构体成员结构 {
    struct 单词结构 *成员单词;
    int 偏移量;
    int 数据类型;
    struct 结构体成员结构 *关联的结构体;
} **全_结构体成员列表; // array (indexed by 数据类型) of struct member lists

struct label修补结构 {
    int *地址; // 指向 全_代码段 中需要修补的位置（即 JMP 的操作数位置）
    struct label修补结构 *下个修补点;
};

// 用于goto标签管理
struct label结构 {
    char *名称;
    int 是否已定义; //
    int 目标地址; //
    struct label修补结构 *待修补应用点列表; // 所有待修补的引用点
    struct label结构 *下个标签; //
} *全_标签列表;

// 用于跟踪未解析的函数引用
struct 函数修补结构 {
    int *地址; // 指向 全_代码段 中需要修补的位置
    struct 单词结构 *函数标识符; //
    struct 函数修补结构 *下个待修补引用点; //
} *全_函数修补列表;

// 单词编码（运算符排在最后，并按优先级顺序排列）
enum {
    标识类型_数字 = 128, 标识类型_函数, 标识类型_系统, 标识类型_全局, 标识类型_局部, 标识类型_标识符,标识类型_数组,

    Break,TOK_跳出, Case,TOK_分支, Char,TOK_字符, Int8, TOK_整数8, Int16, TOK_整数16, Int32, TOK_整数32, Int64, TOK_整数64,
    Default,TOK_默认, Else,TOK_否则, Enum,TOK_枚举, If, TOK_如果, Int,TOK_整数,
    Return,TOK_返回, Sizeof,TOK_取大小, Struct,TOK_结构体, Switch,TOK_选择, Goto, TOK_跳转 ,TOK_判断,While,

    标点_赋值号, 标点_问号, 标点_逻辑或, 标点_逻辑与, 标点_按位或, 标点_按位异或, 标点_按位与, 标点_双等号, 标点_不等号, 标点_小于号, 标点_大于号, 标点_小于等于号,
    标点_大于等于号, 标点_按位左移, 标点_按位右移, 标点_加号, 标点_减号, 标点_星号, 标点_除号, 标点_百分号, 标点_自增号, 标点_自减号, 标点_圆点, 标点_右箭头, 标点_左方括号
};

// 全_操作码 (本编译器有设置操作码最多占用4个字符，因为中文在UTF-8编码状态下宽度是不固定的，所以这里暂时不能改成中文，当然在GBK状态下中文宽度是固定的是可以把操作码改成中文。)
enum {
    LLO , //加载局部变量（OP_加载局部变量 2：将栈往上的2个位置变量加载到寄存器）。去局部变量地址,以[PC+1]地址,以bp为基址,地址载入累加器a
    LGL , //加加载到全局变量局变量（将代码区位置指定的代码加载到寄存器） 将某个数放入寄存器 寄存器变量 中。加载到全局变量 [PC+1]作为为立即数载入累加器a
    JMP , //跳转指令（OP_跳转 2：将代码区跳转到2这个位置）。无条件跳转到[PC+1]
    JSR , //跳转到子程序（OP_跳到子程序 2：将下一个代码区位置保存到栈顶，并将代码区跳转到2这个位置）。将PC+2入栈作为返回地址,跳转到[PC+1]
    ZJ  , //零跳转（OP_零跳转  2：如果全局变量为a，则跳转到下一个代码区域，否则跳转到2这个位置）。累加器为零时分支,当累加器a为0时跳转到[PC+1],不为零时跳转到PC+2继续执行
    NZJ , //非零跳转(OP_非零跳转 2:如果全局变量不为a，则跳转到下一个代码区域，否则跳转到2这个位置)。累加器不为零时分支,当累加器a不为0时跳转到[PC+1],为零时跳转到PC+2
    ENT , //开辟一个新的子程序(OP_进入子程序 2:保存前一个程序的栈顶到当前栈上，并设置新栈顶，和局部变量区)。进入子程序,将bp压栈,基址bp指向栈顶,然后将栈顶生长[PC+1]字,作为参数传递空间
    SJP , //栈跳转(OP_栈跳转 2：栈调整，往前2个位置)
    LEV , //子程序退出(栈回滚到当前程序的栈顶，然后恢复上一个程序的栈顶，再恢复上一个程序的代码区，最后恢复上一个程序的栈)。离开子程序,堆栈指针sp = 基址,从堆栈中弹出基址bp,全_下条指令
    LI  , //加载整数(将寄存器的整数变量加载出来).将对应地址中的整数载入 寄存器变量 中，要求 寄存器变量 中存放地址。以a为地址取int数
    LI8 ,
    LI16,
    LI32,
    LI64,
    LC  , //加载字符(将寄存器的字符变量加载出来).将对应地址中的字符载入 寄存器变量 中，要求 寄存器变量 中存放地址。以a为地址取char
    SI  , //保存整数(将栈顶指定位置的地址保存整数).将 寄存器变量 中的数据作为整数存放入地址中，要求栈顶存放地址。以栈顶为地址存int数并弹栈[[全_栈顶++]]=a
    SI8 ,
    SI16,
    SI32,
    SI64,
    SC  , //保存字符(将栈顶指定位置的地址保存字符).将 寄存器变量 中的数据作为字符存放入地址中，要求栈顶存放地址。以栈顶为地址存char并弹栈[[全_栈顶++]]=a
    PSH , //保存栈(将寄存器变量保存到栈上)，将a压栈

    //都是二元操作符，都是将栈顶变量取出来，然后与寄存器运算，再存回到寄存器
    OR  ,
    XOR ,
    AND ,
    EQ  ,
    NE  ,
    LT  ,
    GT  ,
    LE  ,
    GE  ,
    SHL ,
    SHR ,
    ADD ,
    SUB ,
    MUL ,
    DIV ,
    MOD ,

    //以下都是系统调用，将栈顶变量作为参数，逐一打印出来。在符号表上的Class为Sys，Type为Int，Val为特殊值.C4在自举时，会把#include全忽略掉，然后在main()里hack一下，把依赖的外部函数先硬插进符号表里。
    OPEN, //OPEN 调用C库函数open,堆栈传递2个参数(第一个参数先进栈,返回值存入累加器a,下同)
    READ, //READ 调用C库函数read,堆栈传递2个参数
    CREA, //CLOS 调用C库函数close,堆栈传递2个参数
    WRIT,
    LSEE,
    CLOS,
    STRL,
    PRTF, //PRTF 调用C库函数printf,[全_下条指令+1]表明参数个数,传递至多六个参数
    MALC, //MALC 调用C库函数malloc,堆栈传递一个参数
    MSET, //MSET 调用C库函数memset,堆栈传递3个参数
    MCMP, //MCMP 调用C库函数memcmp,堆栈传递3个参数
    REAL,
    FREE,
    MCPY,
    SCPY,
    SNCP,
    SCMP,
    SNCM,
    SCAT,
    SDUP,
    EXIT  //退出整个程序(将栈顶变量打印出来，作为返回值，并退出来)
};


// 支持的数据类型
enum 数据类型
{
    TYPE_CHAR, TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_INT64, TYPE_INT, TYPE_PTR = 256, TYPE_PTR2 = 512,
    TYPE_ARRAY = 1024
};


int 检查函数声明(struct 单词结构 *函数标识符, int 参数数量, int 返回类型);
// ======================
// FNV-1a哈希函数 经过多维度对比目前最适合做编译器词法分析的字符串哈希函数
// ======================

//64位版 FNV-1a 哈希函数
//#define FNV_PRIME_64 1099511628211ULL
//#define FNV_OFFSET_BASIS_64 14695981039346656037ULL
//uint64_t hash = fnv1aHASH64(str);
//size_t index = hash % table_size;  // 取模后可以作为哈希表索引，table_size 通常选大质数
uint64_t fnv1aHASH64(char *str)
{
    uint64_t 哈希值;
    char c;

    哈希值 = 0xCBF29CE484222325;
    while ((c = *str++))
    {
        哈希值 = 哈希值 ^ c;
        哈希值 = 哈希值 * 0x100000001B3;
    }
    return 哈希值;
}

int 是标识符(char 符)
{
    return (符 >= 'a' && 符 <= 'z') || (符 >= 'A' && 符 <= 'Z') || 符 == '_';
}
int 是数字(char 符)
{
    return 符 >= '0' && 符 <= '9';
}
void 取下个单词()
{
    uint8_t *首字符;
    uint64_t 哈希值64;
    int 标识符长度;
    char *单词字符串;

    标识符长度 = 0;

    单词字符串 = malloc(sizeof(char) * 标识符最大长度);
    memset(单词字符串, 0, 标识符最大长度);

    //逐个读取字符，主函数中已经把源代码读入了“ 全_源码指针 ”，以`\0`结尾。现在获取当前字符。当前字符不是`\0`，识别到`\0` 结束循环。
    //用循环来忽略空白字符,不过不能被词法分析器识别的字符都被认为是空白字符 比如 '@', '$'
    while (全_单词编码 = 全_当前字符 = *全_源码指针)
    {
        ++全_源码指针; // 指向下一个字符。

        switch (全_当前字符)
        {
            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
            case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
            case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':
            case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
            case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
            case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V':
            case 'W': case 'X': case 'Y': case 'Z': case '_':

            解析标识符:
                首字符 = 全_源码指针 - 1;
                while (是标识符(*全_源码指针) || 是数字(*全_源码指针) || (*全_源码指针 >= 0x80 && *全_源码指针 <= 0xFF))
                {
                    *全_源码指针++;
                }

                标识符长度 = 全_源码指针 - 首字符;

                strncpy(单词字符串, 首字符, 标识符长度);
                单词字符串[标识符长度] = '\0';
                哈希值64 = fnv1aHASH64(单词字符串);

                全_当前单词 = 全_单词表;
                while (全_当前单词->单词编码) //遍历符号表中的所有已经添加的条目
                {
                    if (哈希值64 == 全_当前单词->哈希值 && !memcmp(全_当前单词->字符串, 首字符, 全_源码指针 - 首字符)) //1.先比较哈希值，如果相同再比较字符串内容;2.如果找到匹配项，直接返回已存在的标识符类型
                    {
                        全_单词编码 = 全_当前单词->单词编码;
                        return;
                    }
                    全_当前单词 = 全_当前单词 + 1; // 指向下一个标识符.如果找不到匹配项，则在此处创建一个新的标识符
                }

                全_当前单词->字符串 = malloc(标识符长度 + 1);
                strncpy(全_当前单词->字符串, 首字符, 标识符长度);
                全_当前单词->字符串[标识符长度] = '\0';
                全_当前单词->哈希值 = 哈希值64;
                全_单词编码 = 全_当前单词->单词编码 = 标识类型_标识符;

                return;

            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7':
            case '8': case '9':
                if (全_单词的值 = 全_当前字符 - '0')
                {
                    while (*全_源码指针 >= '0' && *全_源码指针 <= '9')
                    {
                        全_单词的值 = 全_单词的值 * 10 + *全_源码指针++ - '0'; //第一位不为0,认为是十进制数
                    }
                }else if (*全_源码指针 == 'x' || *全_源码指针 == 'X') //第一位为0,且以x开头,认为是16进制数
                { // 将十六进制表示法转换为值。
                    while ((全_当前字符 = *++全_源码指针) && ((全_当前字符 >= '0' && 全_当前字符 <= '9') || (全_当前字符 >= 'a' && 全_当前字符 <= 'f') || (全_当前字符 >= 'A' && 全_当前字符 <= 'F')))
                    {
                        全_单词的值 = 全_单词的值 * 16 + (全_当前字符 & 15) + (全_当前字符 >= 'A' ? 9 : 0);
                    }
                }else
                { //八进制：如果当前字符是 `0` 并且后面的字符不是 `x` 或`X`，它是八进制表示法。将八进制表示法转换为值。
                    while (*全_源码指针 >= '0' && *全_源码指针 <= '7')
                    {
                        全_单词的值 = 全_单词的值 * 8 + *全_源码指针++ - '0';
                    }
                }
                全_单词编码 = 标识类型_数字;
                return;
            case '\n':
                if (全_打印源码) //命令行指明-s参数,输出源代码和对应字节码
                {
                    // 打印源代码行
                    printf("%d: %.*s", 全_行号, 全_源码指针 - l_全_源码指针, l_全_源码指针);
                    l_全_源码指针 = 全_源码指针; // 将 `l_全_源码指针` 指向最后一个换行符。
                    while (l_全_代码段 < 全_代码段)
                    {
                        // 打印操作码。
                        printf("%8.4s", &全_操作码[*++l_全_代码段 * 5]); //确保操作码在8个字符宽度内右对齐显示，但最多只显示4个字符.
                        // 如果操作码 <= 栈跳转，它有操作数。
                        // 打印操作数。
                        if (*l_全_代码段 <= SJP)
                            printf(" %d\n", *++l_全_代码段);
                        else
                            printf("\n");
                    }
                }
                ++全_行号;
                //此处没有使用break，继续处理下一个case，直至遇到break,return,goto...
            case ' ': case '\t': case '\v': case '\f': case '\r':
                break;
            case '#':

                while (*全_源码指针 != 0 && *全_源码指针 != '\n')
                {
                    ++全_源码指针;
                }
                break;
            case '/':
                if (*全_源码指针 == '/') // 单行注释
                {
                    while (*全_源码指针 != 0 && *全_源码指针 != '\n')
                    {
                        ++全_源码指针;
                    }
                } else if(*全_源码指针 == '*') // 多行注释处理
                {
                    全_源码指针++;
                    while (*全_源码指针 && !(*全_源码指针 == '*' && *(全_源码指针 + 1) == '/'))
                    {
                        if (*全_源码指针 == '\n') 全_行号++;
                        全_源码指针++;
                    }
                    if (*全_源码指针) 全_源码指针 = 全_源码指针 + 2;
                }else
                { // 除法
                    全_单词编码 = 标点_除号;
                    return;
                }
                break;
            case '\'':
            case '"': //引号开头,认为是字符(串).// 6、解析字符串
                首字符 = 全_数据段; //判断双引号和单引号，存储数据缓冲区的当前位置。
                while (*全_源码指针 != 0 && *全_源码指针 != 全_当前字符) // 当前字符不是`\0`并且当前字符不是引号字符。
                { //直到找到匹配的引号为止
                    if ((全_单词的值 = *全_源码指针++) == '\\') // 解析转义字符
                    {
                        // 如果后面的字符是`n`，则是换行符，
                        switch (全_单词的值 = *全_源码指针++)
                        {
                            case 'n': 全_单词的值 = '\n'; break;
                            case 't': 全_单词的值 = '\t'; break;
                            case 'v': 全_单词的值 = '\v'; break;
                            case 'f': 全_单词的值 = '\f'; break;
                            case 'r': 全_单词的值 = '\r'; break;
                            default: break;
                        }
                    }
                    // 如果是字符串常量，则将当前字符复制到数据缓冲区。
                    if (全_当前字符 == '"')
                    {
                        *全_数据段++ = 全_单词的值;
                    }
                }
                ++全_源码指针;
                if (全_当前字符 == '"') // 如果是字符串常量，则使用字符串的地址作为令牌的地址关联值。令牌类型为`"`。如果是字符常量，则使用字符的值作为tokens标记的值关联值。将令牌类型设置为数字常量。
                {
                    全_单词的值 = (int)首字符; //双引号则"当前单词编码的值"指向data_section中字符串开始,单引号则认为是数字
                }else
                {
                    全_单词编码 = 标识类型_数字;
                }
                return;

                // 以下为：解析运算符号，和指令一一对应
            case '=':
                if (*全_源码指针 == '=')
                {
                    ++全_源码指针;
                    全_单词编码 = 标点_双等号;
                } else
                {
                    全_单词编码 = 标点_赋值号;
                }
                return;
            case '+':
                if (*全_源码指针 == '+')
                { ++全_源码指针; 全_单词编码 = 标点_自增号; } else 全_单词编码 = 标点_加号; return;
            case '-':
                if (*全_源码指针 == '-')
                { ++全_源码指针; 全_单词编码 = 标点_自减号; } else if (*全_源码指针 == '>') { ++全_源码指针; 全_单词编码 = 标点_右箭头; } else 全_单词编码 = 标点_减号; return;
            case '!':
                if (*全_源码指针 == '=')
                {
                    ++全_源码指针;
                    全_单词编码 = 标点_不等号;
                }
                return;
            case '<':
                if (*全_源码指针 == '=')
                { ++全_源码指针; 全_单词编码 = 标点_小于等于号; } else if (*全_源码指针 == '<') { ++全_源码指针; 全_单词编码 = 标点_按位左移; } else 全_单词编码 = 标点_小于号; return;
            case '>':
                if (*全_源码指针 == '=')
                { ++全_源码指针; 全_单词编码 = 标点_大于等于号; } else if (*全_源码指针 == '>') { ++全_源码指针; 全_单词编码 = 标点_按位右移; } else 全_单词编码 = 标点_大于号; return;
            case '|':
                if (*全_源码指针 == '|')
                { ++全_源码指针; 全_单词编码 = 标点_逻辑或; } else 全_单词编码 = 标点_按位或; return;
            case '&':
                if (*全_源码指针 == '&')
                { ++全_源码指针; 全_单词编码 = 标点_逻辑与; } else 全_单词编码 = 标点_按位与; return;
            case '^':
                全_单词编码 = 标点_按位异或;
                return;
            case '%':
                全_单词编码 = 标点_百分号;
                return;
            case '*':
                全_单词编码 = 标点_星号;
                return;
            case '[':
                全_单词编码 = 标点_左方括号;
                return;
            case '?':
                全_单词编码 = 标点_问号;
                return;
            case '.':
                全_单词编码 = 标点_圆点;
                return;
            default:
                if(全_当前字符 >= 0x80 && 全_当前字符 <= 0xFF)
                {
                    goto 解析标识符;
                }
                全_单词编码 = 全_当前字符;
//                printf("%s第%d行: 无法识别的字符 %c : %c\n", 全_源文件名称,全_行号, 全_当前字符, 全_单词编码);

                return;
        }
    }
    free(单词字符串);
}

// 查找标签
struct label结构 *查找标签(char *名称)
{
    struct label结构 *标签;
    标签 = 全_标签列表;
    while (标签)
    {
        if (strcmp(标签->名称, 名称) == 0)
        {
            return 标签;
        }
        标签 = 标签->下个标签;
    }
    return ((void *)0);
}

// 添加标签
struct label结构 *添加标签(char *名称)
{
    struct label结构 *标签;
    标签 = malloc(sizeof(struct label结构));
    标签->名称 = strdup(名称); /* 它会为原字符串分配一块新的内存，并将原字符串的内容复制到这块新内存中，然后返回指向该新内存的指针。 */
    标签->是否已定义 = 0;
    标签->目标地址 = 0;
    标签->待修补应用点列表 = ((void *)0);
    标签->下个标签 = 全_标签列表;
    全_标签列表 = 标签;
    return 标签;
}

//解析表达式并生成对应的虚拟机代码。支持运算符优先级和递归下降法。`level`：表示运算符,因为各个运算符token是按照优先级生序排列的,所以lev大表示优先级高.根据不同的预算符，将变量计算，并将结果均写入到寄存器中。
//解析表达式()负责运算符语句的语法分析，包括加减乘除赋值等等
void 解析表达式(int 优先级)
{
    int t,   //参数计数
    *b,
            sz;
    struct 单词结构 *正在解析的单词;
    struct 结构体成员结构 *结构体成员;
    struct 函数修补结构 *函数修补点;

    switch (全_单词编码) //此时 全_单词编码 =
    {
        case 0:  // 1、如果当前单词不存在，则打印错误并退出程序。
            printf("%d: 表达式未完成，意外结束\n", 全_行号);
            exit(-1);
        case 标识类型_数字:  //2、解析数字表达式.直接取立即数作为表达式值，用 “OP_加载全局变量” 指令将它加载到 AX 中
            *++全_代码段 = LGL;
            *++全_代码段 = 全_单词的值;
            取下个单词();
            全_声明数据类型 = TYPE_INT;

            break;
        case '"': //3、解析字符串表达式.将字符串的起始地址作为表达式值，用 “LGL” 指令将它加载到 AX 中
            *++全_代码段 = LGL;
            *++全_代码段 = 全_单词的值; // 字符串的起始地址 （词法分析的时候遇到'"'会处理获取"全_单词的值"）
            取下个单词(); //跳过字符串开始的'"'
            while (全_单词编码 == '"') // 当前tokens标记为字符串常量时，它是相邻字符串常量，例如“ABC”“def”。在 `取下个单词` 中，字符串的字符已被复制到数据缓冲区。这实现了相邻字符串常量的连接。//连续的'"' 处理C风格多行文本 比如["abc" "def"]
                取下个单词();
            全_数据段 = (char *)((int)全_数据段 + sizeof(int) & -sizeof(int)); //字节对齐到int，在末尾追加结束符。// 将 `数据的段` 指向下一个 int 对齐的地址。例如`-sizeof(int)` 是 -4，即 0b11111100。这保证在字符串后至少留下一个 '\0'。设置结果值类型为源代码字符串指针。数据类型_字符 + 数据类型_指针 = 数据类型_指针 因为 数据类型_字符 是 0。
            全_声明数据类型 = TYPE_PTR;
            break;
        case Sizeof:
        case TOK_取大小:  //4、解析sizeof表达式
            取下个单词();
            if (全_单词编码 == '(')
            {
                取下个单词();
            }else
            {
                printf("%d: sizeof之后应该跟一个左圆括号。\n", 全_行号);
                exit(-1);
            }
            全_声明数据类型 = TYPE_INT;
            if (全_单词编码 == Int || 全_单词编码 == TOK_整数)
                取下个单词();
            else if (全_单词编码 == Char || 全_单词编码 == TOK_字符)
            {
                取下个单词();
                全_声明数据类型 = TYPE_CHAR;
            }else if (全_单词编码 == Int8 || 全_单词编码 == TOK_整数8)
            {
                取下个单词();
                全_声明数据类型 = TYPE_INT8;
            }else if (全_单词编码 == Int16 || 全_单词编码 == TOK_整数16)
            {
                取下个单词();
                全_声明数据类型 = TYPE_INT16;
            }else if (全_单词编码 == Int32 || 全_单词编码 == TOK_整数32)
            {
                取下个单词();
                全_声明数据类型 = TYPE_INT32;
            }else if (全_单词编码 == Int64 || 全_单词编码 == TOK_整数64)
            {
                取下个单词();
                全_声明数据类型 = TYPE_INT64;
            }else if (全_单词编码 == Struct || 全_单词编码 == TOK_结构体)
            {
                取下个单词();
                if (全_单词编码 != 标识类型_标识符)
                {
                    printf("%d: 错误的结构体类型\n", 全_行号);
                    exit(-1);
                }
                全_声明数据类型 = 全_当前单词->表示的结构体名称;
                取下个单词();
            }
            while (全_单词编码 == 标点_星号)
            {
                取下个单词();
                全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
            }
            if (全_单词编码 == ')')
                取下个单词();
            else
            {
                printf("%d: 此时应该为sizeof的右圆括号。\n", 全_行号);
                exit(-1);
            }
            *++全_代码段 = LGL;  //添加`LGL`指令以加载操作数值的大小到寄存器。
            *++全_代码段 = 全_声明数据类型 >= TYPE_PTR ? sizeof(int) : 全_数据类型大小[全_声明数据类型];
            全_声明数据类型 = TYPE_INT;
            break;
        case 标识类型_标识符:
            正在解析的单词 = 全_当前单词; //全_当前单词 赋值给 正在解析的单词
            取下个单词(); //跳过标识符
            if (全_单词编码 == '(') //解析函数调用表达式.
            {
                取下个单词(); //跳过左圆括号
                t = 0;
                while (全_单词编码 != ')') //函数实参列表
                {
                    解析表达式(标点_赋值号);
                    *++全_代码段 = PSH;
                    ++t;
                    if (全_单词编码 == ',')
                        取下个单词();
                }
                取下个单词(); //跳过右圆括号
                // 在 解析表达式 函数中处理函数调用的部分，替换为以下代码：
                if (正在解析的单词->声明类型 == 标识类型_系统)
                {
                    *++全_代码段 = 正在解析的单词->地址;
                }else if (正在解析的单词->声明类型 == 标识类型_函数)
                {
                    *++全_代码段 = JSR;
                    *++全_代码段 = 正在解析的单词->地址;
                }else if (正在解析的单词->声明类型 == 0 || 正在解析的单词->声明类型 == 标识类型_标识符) // 添加对函数前置调用的支持
                {
                    // 假设这是一个函数声明，先标记为未定义函数
                    正在解析的单词->声明类型 = 标识类型_函数;
                    *++全_代码段 = JSR;

                    // 添加到函数修补列表
                    函数修补点 = malloc(sizeof(struct 函数修补结构));
                    函数修补点->地址 = ++全_代码段;
                    函数修补点->函数标识符 = 正在解析的单词;
                    函数修补点->下个待修补引用点 = 全_函数修补列表;
                    全_函数修补列表 = 函数修补点;

                    // 临时存储0，后续会回填
                    *函数修补点->地址 = 0;
                }else
                {
                    printf("%d: 错误的函数调用（函数未定义）\n", 全_行号);
                    exit(-1);
                }
                if (t)
                {
                    *++全_代码段 = SJP; //调整栈
                    *++全_代码段 = t;
                }
                全_声明数据类型 = 正在解析的单词->数据类型;
            }else if (正在解析的单词->声明类型 == 标识类型_数字) //5、解析数字表达式.
            {
                *++全_代码段 = LGL;
                *++全_代码段 = 正在解析的单词->地址;
                全_声明数据类型 = TYPE_INT;
            }else
            {
                if (正在解析的单词->声明类型 == 标识类型_局部)
                {
                    *++全_代码段 = LLO;
                    *++全_代码段 = 全_局部变量偏移 - 正在解析的单词->地址;
                }else if (正在解析的单词->声明类型 == 标识类型_全局)
                {
                    *++全_代码段 = LGL;
                    *++全_代码段 = 正在解析的单词->地址;
                }else
                {
                    printf("%d: 未定义的变量 \n", 全_行号);
                    exit(-1);
                }
                if ((全_声明数据类型 = 正在解析的单词->数据类型) <= TYPE_INT || 全_声明数据类型 >= TYPE_PTR)
                {

                    if(全_声明数据类型 == TYPE_INT8)
                    {
                        *++全_代码段 = LI8;
                    }else if(全_声明数据类型 == TYPE_INT16)
                    {
                        *++全_代码段 = LI16;
                    }else if(全_声明数据类型 == TYPE_INT32)
                    {
                        *++全_代码段 = LI32;
                    }else if(全_声明数据类型 == TYPE_INT64)
                    {
                        *++全_代码段 = LI64;
                    }else
                    {
                        *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? LC : LI;
                    }
                }

            }
            break;
        case '(':
            取下个单词();
            if (全_单词编码 == Int || 全_单词编码 == TOK_整数 ||
                全_单词编码 == Int8 || 全_单词编码 == TOK_整数8 ||
                全_单词编码 == Int16 || 全_单词编码 == TOK_整数16 ||
                全_单词编码 == Int32 || 全_单词编码 == TOK_整数32 ||
                全_单词编码 == Int64 || 全_单词编码 == TOK_整数64 ||
                全_单词编码 == Char ||  全_单词编码 == TOK_字符 ||
                全_单词编码 == Struct || 全_单词编码 == TOK_结构体)
            {
                if (全_单词编码 == Int || 全_单词编码 == TOK_整数)
                {
                    取下个单词();
                    t = TYPE_INT;
                } else if (全_单词编码 == Char || 全_单词编码 == TOK_字符)
                {
                    取下个单词();
                    t = TYPE_CHAR;
                }else if (全_单词编码 == Int8 || 全_单词编码 == TOK_整数8)
                {
                    取下个单词();
                    t = TYPE_INT8;
                }else if (全_单词编码 == Int16 || 全_单词编码 == TOK_整数16)
                {
                    取下个单词();
                    t = TYPE_INT16;
                }else if (全_单词编码 == Int32 || 全_单词编码 == TOK_整数32)
                {
                    取下个单词();
                    t = TYPE_INT32;
                }else if (全_单词编码 == Int64 || 全_单词编码 == TOK_整数64)
                {
                    取下个单词();
                    t = TYPE_INT64;
                }else
                {
                    取下个单词();
                    if (全_单词编码 != 标识类型_标识符)
                    {
                        printf("%d: 无效的结构体类型 \n", 全_行号);
                        exit(-1);
                    }
                    t = 全_当前单词->表示的结构体名称; 取下个单词();
                }
                while (全_单词编码 == 标点_星号) { 取下个单词(); t = t + TYPE_PTR; }
                if (全_单词编码 == ')') 取下个单词(); else { printf("%d: 无效类型转换 \n", 全_行号); exit(-1); }
                解析表达式(标点_自增号);
                全_声明数据类型 = t;
            }
            else {
                解析表达式(标点_赋值号);
                if (全_单词编码 == ')') 取下个单词(); else { printf("%d: 应为右圆括号 \n", 全_行号); exit(-1); }
            }
            break;
        case 标点_星号:
            取下个单词();
            解析表达式(标点_自增号);
            if (全_声明数据类型 > TYPE_INT)
                全_声明数据类型 = 全_声明数据类型 - TYPE_PTR;
            else
            {
                printf("%d: 无效解引用 \n", 全_行号);
                exit(-1);
            }
            if (全_声明数据类型 <= TYPE_INT || 全_声明数据类型 >= TYPE_PTR)
            {
                if(全_声明数据类型 == TYPE_INT8)
                {
                    *++全_代码段 = LI8;
                }else if(全_声明数据类型 == TYPE_INT16)
                {
                    *++全_代码段 = LI16;
                }else if(全_声明数据类型 == TYPE_INT32)
                {
                    *++全_代码段 = LI32;
                }else if(全_声明数据类型 == TYPE_INT64)
                {
                    *++全_代码段 = LI64;
                }else
                {
                    *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? LC : LI;
                }
            }

            break;
        case 标点_按位与:
            取下个单词(); 解析表达式(标点_自增号);
            if (*全_代码段 == LC || *全_代码段 == LI || *全_代码段 == LI8 || *全_代码段 == LI16 || *全_代码段 == LI32 || *全_代码段 == LI64)
            {
                --全_代码段; // XXX else { printf("%d: bad 目标地址-of\n", 全_行号); exit(-1); }
            }
            全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
            break;
        case '!': 取下个单词(); 解析表达式(标点_自增号); *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = 0; *++全_代码段 = EQ; 全_声明数据类型 = TYPE_INT; break;
        case '~': 取下个单词(); 解析表达式(标点_自增号); *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = -1; *++全_代码段 = XOR; 全_声明数据类型 = TYPE_INT; break;
        case 标点_加号: 取下个单词(); 解析表达式(标点_自增号); 全_声明数据类型 = TYPE_INT; break;
        case 标点_减号:
            取下个单词(); *++全_代码段 = LGL;
            if (全_单词编码 == 标识类型_数字) { *++全_代码段 = -全_单词的值; 取下个单词(); } else { *++全_代码段 = -1; *++全_代码段 = PSH; 解析表达式(标点_自增号); *++全_代码段 = MUL; }
            全_声明数据类型 = TYPE_INT;
            break;
        case 标点_自增号:
        case 标点_自减号:
            t = 全_单词编码; 取下个单词(); 解析表达式(标点_自增号);
            if (*全_代码段 == LC) { *全_代码段 = PSH; *++全_代码段 = LC; }
            else if (*全_代码段 == LI)
            {
                *全_代码段 = PSH;
                *++全_代码段 = LI;
            }else if (*全_代码段 == LI8)
            {
                *全_代码段 = PSH;
                *++全_代码段 = LI8;
            }else if (*全_代码段 == LI16)
            {
                *全_代码段 = PSH;
                *++全_代码段 = LI16;
            }else if (*全_代码段 == LI32)
            {
                *全_代码段 = PSH;
                *++全_代码段 = LI32;
            }else if (*全_代码段 == LI64)
            {
                *全_代码段 = PSH;
                *++全_代码段 = LI64;
            }
            else
            {
                printf("%d: 前置自增运算中左值无效 \n", 全_行号);
                exit(-1);
            }
            *++全_代码段 = PSH;
            *++全_代码段 = LGL; *++全_代码段 = 全_声明数据类型 >= TYPE_PTR2 ? sizeof(int) : (全_声明数据类型 >= TYPE_PTR) ? 全_数据类型大小[全_声明数据类型 - TYPE_PTR] : 1;
            *++全_代码段 = (t == 标点_自增号) ? ADD : SUB;
            if(全_声明数据类型 == TYPE_INT8)
            {
                *++全_代码段 = SI8;
            }else if(全_声明数据类型 == TYPE_INT16)
            {
                *++全_代码段 = SI16;
            }else if(全_声明数据类型 == TYPE_INT32)
            {
                *++全_代码段 = SI32;
            }else if(全_声明数据类型 == TYPE_INT64)
            {
                *++全_代码段 = SI64;
            }else
            {
                *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? SC : SI;
            }

            break;
        default:
            printf("%d: 无效的表达式 \n", 全_行号);
            exit(-1);
    }

    while (全_单词编码 >= 优先级)
    { // 自顶向下运算符优先级法
        t = 全_声明数据类型;
        switch (全_单词编码)
        {
            case 标点_赋值号:
                取下个单词(); // 跳过赋值号
                if (*全_代码段 == LC || *全_代码段 == LI || *全_代码段 == LI8 || *全_代码段 == LI16 || *全_代码段 == LI32 || *全_代码段 == LI64)
                    *全_代码段 = PSH;
                else
                {
                    printf("%d: 在赋值操作中左值无效 \n", 全_行号);
                    exit(-1);
                }
                解析表达式(标点_赋值号);
                if((全_声明数据类型 = t) == TYPE_INT8)
                {
                    *++全_代码段 = SI8;
                }else if((全_声明数据类型 = t) == TYPE_INT16)
                {
                    *++全_代码段 = SI16;
                }else if((全_声明数据类型 = t) == TYPE_INT32)
                {
                    *++全_代码段 = SI32;
                }else if((全_声明数据类型 = t) == TYPE_INT64)
                {
                    *++全_代码段 = SI64;
                }else
                {
                    *++全_代码段 = ((全_声明数据类型 = t) == TYPE_CHAR) ? SC : SI;
                }

                break;
            case 标点_问号:
                取下个单词();
                *++全_代码段 = ZJ;
                b = ++全_代码段;
                解析表达式(标点_赋值号);
                if (全_单词编码 == ':')
                    取下个单词();
                else
                {
                    printf("%d: 条件表达式缺少冒号 \n", 全_行号);
                    exit(-1);
                }
                *b = (int)(全_代码段 + 3);
                *++全_代码段 = JMP;
                b = ++全_代码段;
                解析表达式(标点_问号);
                *b = (int)(全_代码段 + 1);
                break;
            case 标点_逻辑或: 取下个单词(); *++全_代码段 = NZJ; b = ++全_代码段; 解析表达式(标点_逻辑与); *b = (int)(全_代码段 + 1); 全_声明数据类型 = TYPE_INT; break;
            case 标点_逻辑与: 取下个单词(); *++全_代码段 = ZJ;  b = ++全_代码段; 解析表达式(标点_按位或);  *b = (int)(全_代码段 + 1); 全_声明数据类型 = TYPE_INT; break;
            case 标点_按位或:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位异或); *++全_代码段 = OR;  全_声明数据类型 = TYPE_INT; break;
            case 标点_按位异或: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位与); *++全_代码段 = XOR; 全_声明数据类型 = TYPE_INT; break;
            case 标点_按位与: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_双等号);  *++全_代码段 = AND; 全_声明数据类型 = TYPE_INT; break;
            case 标点_双等号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_小于号);  *++全_代码段 = EQ;  全_声明数据类型 = TYPE_INT; break;
            case 标点_不等号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_小于号);  *++全_代码段 = NE;  全_声明数据类型 = TYPE_INT; break;
            case 标点_小于号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位左移); *++全_代码段 = LT;  全_声明数据类型 = TYPE_INT; break;
            case 标点_大于号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位左移); *++全_代码段 = GT;  全_声明数据类型 = TYPE_INT; break;
            case 标点_小于等于号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位左移); *++全_代码段 = LE;  全_声明数据类型 = TYPE_INT; break;
            case 标点_大于等于号:  取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_按位左移); *++全_代码段 = GE;  全_声明数据类型 = TYPE_INT; break;
            case 标点_按位左移: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_加号); *++全_代码段 = SHL; 全_声明数据类型 = TYPE_INT; break;
            case 标点_按位右移: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_加号); *++全_代码段 = SHR; 全_声明数据类型 = TYPE_INT; break;
            case 标点_加号:
                取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_星号);
                sz = (全_声明数据类型 = t) >= TYPE_PTR2 ? sizeof(int) : 全_声明数据类型 >= TYPE_PTR ? 全_数据类型大小[全_声明数据类型 - TYPE_PTR] : 1;
                if (sz > 1) { *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz; *++全_代码段 = MUL;  }
                *++全_代码段 = ADD;
                break;
            case 标点_减号:
                取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_星号);
                sz = t >= TYPE_PTR2 ? sizeof(int) : t >= TYPE_PTR ? 全_数据类型大小[t - TYPE_PTR] : 1;
                if (t == 全_声明数据类型 && sz > 1) { *++全_代码段 = SUB; *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz; *++全_代码段 = DIV; 全_声明数据类型 = TYPE_INT; }
                else if (sz > 1) { *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz; *++全_代码段 = MUL; *++全_代码段 = SUB; }
                else *++全_代码段 = SUB;
                全_声明数据类型 = t;
                break;
            case 标点_星号: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_自增号); *++全_代码段 = MUL; 全_声明数据类型 = TYPE_INT; break;
            case 标点_除号: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_自增号); *++全_代码段 = DIV; 全_声明数据类型 = TYPE_INT; break;
            case 标点_百分号: 取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_自增号); *++全_代码段 = MOD; 全_声明数据类型 = TYPE_INT; break;
            case 标点_自增号: case 标点_自减号:
                if (*全_代码段 == LC) { *全_代码段 = PSH; *++全_代码段 = LC; }
                else if (*全_代码段 == LI)
                {
                    *全_代码段 = PSH;
                    *++全_代码段 = LI;
                }
                else if (*全_代码段 == LI8)
                {
                    *全_代码段 = PSH;
                    *++全_代码段 = LI8;
                }
                else if (*全_代码段 == LI16)
                {
                    *全_代码段 = PSH;
                    *++全_代码段 = LI16;
                }else if (*全_代码段 == LI32)
                {
                    *全_代码段 = PSH;
                    *++全_代码段 = LI32;
                }else if (*全_代码段 == LI64)
                {
                    *全_代码段 = PSH;
                    *++全_代码段 = LI64;
                }
                else
                {
                    printf("%d: 后置自增运算中左值无效 \n", 全_行号);
                    exit(-1);
                }
                sz = 全_声明数据类型 >= TYPE_PTR2 ? sizeof(int) : 全_声明数据类型 >= TYPE_PTR ? 全_数据类型大小[全_声明数据类型 - TYPE_PTR] : 1;
                *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz;
                *++全_代码段 = (全_单词编码 == 标点_自增号) ? ADD : SUB;
                if(全_声明数据类型 == TYPE_INT8)
                {
                    *++全_代码段 = SI8;
                }else if(全_声明数据类型 == TYPE_INT16)
                {
                    *++全_代码段 = SI16;
                }else if(全_声明数据类型 == TYPE_INT32)
                {
                    *++全_代码段 = SI32;
                }else if(全_声明数据类型 == TYPE_INT64)
                {
                    *++全_代码段 = SI64;
                }else
                {
                    *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? SC : SI;
                }

                *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz;
                *++全_代码段 = (全_单词编码 == 标点_自增号) ? SUB : ADD;
                取下个单词();
                break;
            case 标点_圆点:
                全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
                // 注意：这里没有break，继续执行标点_右箭头的情况
            case 标点_右箭头:
                if (全_声明数据类型 <= TYPE_PTR+TYPE_INT || 全_声明数据类型 >= TYPE_PTR2) { printf("%d: 应该是结构体\n", 全_行号); exit(-1); }
                取下个单词();
                if (全_单词编码 != 标识类型_标识符) { printf("%d: 应该是结构体成员 \n", 全_行号); exit(-1); }
                结构体成员 = 全_结构体成员列表[全_声明数据类型 - TYPE_PTR]; while (结构体成员 && 结构体成员->成员单词 != 全_当前单词) 结构体成员 = 结构体成员->关联的结构体;
                if (!结构体成员) { printf("%d: 未发现结构体成员 \n", 全_行号); exit(-1); }
                if (结构体成员->偏移量) { *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = 结构体成员->偏移量; *++全_代码段 = ADD; }
                全_声明数据类型 = 结构体成员->数据类型;
                if (全_声明数据类型 <= TYPE_INT || 全_声明数据类型 >= TYPE_PTR)
                {
                    if(全_声明数据类型 == TYPE_INT8)
                    {
                        *++全_代码段 = LI8;
                    }else if(全_声明数据类型 == TYPE_INT16)
                    {
                        *++全_代码段 = LI16;
                    }else if(全_声明数据类型 == TYPE_INT32)
                    {
                        *++全_代码段 = LI32;
                    }else if(全_声明数据类型 == TYPE_INT64)
                    {
                        *++全_代码段 = LI64;
                    }else
                    {
                        *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? LC : LI;
                    }

                }

                取下个单词();
                break;
            case 标点_左方括号:
                取下个单词(); *++全_代码段 = PSH; 解析表达式(标点_赋值号);
                if (全_单词编码 == ']') 取下个单词(); else { printf("%d: 应为右方括号\n", 全_行号); exit(-1); }
                if (t < TYPE_PTR) { printf("%d: 应为指针的类型 \n", 全_行号); exit(-1); }
                sz = (t = t - TYPE_PTR) >= TYPE_PTR ? sizeof(int) : 全_数据类型大小[t];
                if (sz > 1) { *++全_代码段 = PSH; *++全_代码段 = LGL; *++全_代码段 = sz; *++全_代码段 = MUL;  }
                *++全_代码段 = ADD;
                if ((全_声明数据类型 = t) <= TYPE_INT || 全_声明数据类型 >= TYPE_PTR)
                {
                    if(全_声明数据类型 == TYPE_INT8)
                    {
                        *++全_代码段 = LI8;
                    }else if(全_声明数据类型 == TYPE_INT16)
                    {
                        *++全_代码段 = LI16;
                    }else if(全_声明数据类型 == TYPE_INT32)
                    {
                        *++全_代码段 = LI32;
                    }else if(全_声明数据类型 == TYPE_INT64)
                    {
                        *++全_代码段 = LI64;
                    }else
                    {
                        *++全_代码段 = (全_声明数据类型 == TYPE_CHAR) ? LC : LI;
                    }

                }

                break;
            default: printf("%d: 编译错误，单词编码 =%d\n", 全_行号, 全_单词编码); exit(-1);
        }
    }
}

void 解析语句()
{
    int *a, *b, *d, i;
    struct label结构 *标签;
    struct 单词结构 *标识符;
    int *跳转地址;
    struct label修补结构 *标签修补;
    struct label修补结构 *标签前向修补;
    struct label修补结构 *临时修补;

    switch (全_单词编码)
    {
        case If:
        case TOK_如果:
            取下个单词();
            if (全_单词编码 == '(')
                取下个单词();
            else
            {
                printf("%d: 应为左圆括号 \n", 全_行号);
                exit(-1);
            }
            解析表达式(标点_赋值号);
            if (全_单词编码 == ')')
                取下个单词();
            else
            {
                printf("%d: 应为右圆括号 \n", 全_行号);
                exit(-1);
            }
            *++全_代码段 = ZJ;
            b = ++全_代码段;
            解析语句();
            if (全_单词编码 == Else || 全_单词编码 == TOK_否则)
            {
                *b = (int)(全_代码段 + 3);
                *++全_代码段 = JMP;
                b = ++全_代码段;
                取下个单词();
                解析语句();
            }
            *b = (int)(全_代码段 + 1);
            return;
        case While:
        case TOK_判断:
            取下个单词();
            a = 全_代码段 + 1;
            if (全_单词编码 == '(') 取下个单词(); else { printf("%d: 应为左圆括号 \n", 全_行号); exit(-1); }
            解析表达式(标点_赋值号);
            if (全_单词编码 == ')') 取下个单词(); else { printf("%d: 应为右圆括号 \n", 全_行号); exit(-1); }
            *++全_代码段 = ZJ; b = ++全_代码段;
            解析语句();
            *++全_代码段 = JMP; *++全_代码段 = (int)a;
            *b = (int)(全_代码段 + 1);
            return;
        case Switch:
        case TOK_选择:
            取下个单词();
            if (全_单词编码 == '(') 取下个单词(); else { printf("%d: 应为左圆括号 \n", 全_行号); exit(-1); }
            解析表达式(标点_赋值号);
            if (全_单词编码 == ')') 取下个单词(); else { printf("%d: 应为右圆括号 \n", 全_行号); exit(-1); }
            a = 全_case语句修补; *++全_代码段 = JMP; 全_case语句修补 = ++全_代码段;
            b = 全_break语句修补; d = 全_default语句修补; 全_break语句修补 = 全_default语句修补 = 0;
            解析语句();
            *全_case语句修补 = 全_default语句修补 ? (int)全_default语句修补 : (int)(全_代码段 + 1); 全_case语句修补 = a;
            while (全_break语句修补) { a = (int *)*全_break语句修补; *全_break语句修补 = (int)(全_代码段 + 1); 全_break语句修补 = a; }
            全_break语句修补 = b; 全_default语句修补 = d;
            return;
        case Case:
        case TOK_分支:
            *++全_代码段 = JMP; ++全_代码段; *全_代码段 = (int)(全_代码段 + 7); *++全_代码段 = PSH; i = *全_case语句修补; *全_case语句修补 = (int)全_代码段;
            取下个单词();
            解析表达式(标点_按位或);
            if (全_代码段[-1] != LGL) { printf("%d: 无效的 case 常量 \n", 全_行号); exit(-1); }
            *全_代码段 = *全_代码段 - i; *++全_代码段 = SUB; *++全_代码段 = NZJ; 全_case语句修补 = ++全_代码段; *全_代码段 = i + 全_代码段[-3];
            if (全_单词编码 == ':') 取下个单词(); else { printf("%d: 应为冒号 \n", 全_行号); exit(-1); }
            解析语句();
            return;
        case Break:
        case TOK_跳出:
            取下个单词();
            if (全_单词编码 == ';') 取下个单词(); else { printf("%d: 应为分号 \n", 全_行号); exit(-1); }
            *++全_代码段 = JMP; *++全_代码段 = (int)全_break语句修补; 全_break语句修补 = 全_代码段;
            return;
        case Default:
        case TOK_默认:
            取下个单词();
            if (全_单词编码 == ':') 取下个单词(); else { printf("%d: 应为冒号 \n", 全_行号); exit(-1); }
            全_default语句修补 = 全_代码段 + 1;
            解析语句();
            return;
        case Goto:  // 处理goto语句
        case TOK_跳转:
            取下个单词();
            if (全_单词编码 != 标识类型_标识符) {
                printf("%d: goto 后应为标签名\n", 全_行号);
                exit(-1);
            }
            标签 = 查找标签(全_当前单词->字符串);
            if (!标签) {
                标签 = 添加标签(全_当前单词->字符串);
            }

            // 生成 JMP 指令，操作数暂为 0
            *++全_代码段 = JMP;
            跳转地址 = ++全_代码段; // 记录操作数位置
            *跳转地址 = 0;

            if (标签->是否已定义) {
                // 标签已定义：直接填入地址
                *跳转地址 = 标签->目标地址;
            } else {
                // 标签未定义：加入修补链表
                标签修补 = malloc(sizeof(struct label修补结构));
                标签修补->地址 = 跳转地址;
                标签修补->下个修补点 = 标签->待修补应用点列表;
                标签->待修补应用点列表 = 标签修补;
            }

            取下个单词(); // 跳过标识符
            if (全_单词编码 != ';') {
                printf("%d: goto 语句后应为分号\n", 全_行号);
                exit(-1);
            }
            取下个单词();
            return;
        case Return:
        case TOK_返回:
            取下个单词();
            if (全_单词编码 != ';')
                解析表达式(标点_赋值号);
            *++全_代码段 = LEV;
            if (全_单词编码 == ';')
                取下个单词();
            else
            {
                printf("%d: 应为分号 \n", 全_行号);
                exit(-1);
            }
            return;
        case '{':
            取下个单词();
            while (全_单词编码 != '}')
                解析语句();
            取下个单词();
            return;
        case ';':
            取下个单词();
            return;
        case 标识类型_标识符:
            标识符 = 全_当前单词;
            备份_全_源码指针 = 全_源码指针;   //保存当前标识符位置
            取下个单词();                         // 跳过标识符

            // 检查是否为标签定义 (标识符后跟冒号)
            if (全_单词编码 == ':')
            {
                标签 = 查找标签(标识符->字符串);
                if (!标签)
                {
                    标签 = 添加标签(标识符->字符串);
                }
                if (标签->是否已定义)
                {
                    printf("%d: 重复的标签定义 '%s'\n", 全_行号, 标识符->字符串);
                    exit(-1);
                }
                标签->是否已定义 = 1;
                标签->目标地址 = (int)(全_代码段 + 1); // 下一条指令地址

                // ? 修补所有前向引用
                标签前向修补 = 标签->待修补应用点列表;
                while (标签前向修补)
                {
                    *(标签前向修补->地址) = 标签->目标地址;
                    临时修补 = 标签前向修补;
                    标签前向修补 = 标签前向修补->下个修补点;
                    free(临时修补); // 可选：释放 标签修补 节点
                }

                取下个单词(); // 跳过 ':'
                解析语句();
                return;
            } else
            {
                // 不是标签：必须完全恢复词法分析状态?
                全_源码指针 = 备份_全_源码指针;                    // ? 恢复源码指针
                全_单词编码 = 标识类型_标识符;                   // 恢复 token 类型
                全_当前单词 = 标识符;         // 恢复标识符指针
                // 注意：单词字符串 等临时变量不影响，因为 解析表达式() 会重新调用 取下个单词()
            }
            解析表达式(标点_赋值号);
            if (全_单词编码 == ';')
                取下个单词();
            else
            {
                printf("%d: 应为分号 \n", 全_行号);
                exit(-1);
            }
            return;
        default:
            解析表达式(标点_赋值号);
            if (全_单词编码 == ';')
                取下个单词();
            else
            {
                printf("%d: 应为分号 \n", 全_行号);
                exit(-1);
            }
    }
}



// 回填goto标签地址和未定义函数地址
void 修补labels() {
    struct label结构 *标签;
    int *代码段1; //
    int 操作数1;
    struct 函数修补结构 *函数修补;
    struct 函数修补结构 *临时函数修补;

    代码段1 = 全_代码段;
    while (代码段1 < l_全_代码段) {
        if (*代码段1 == JMP) {
            // 检查操作数是否为标签指针
            操作数1 = *(代码段1 + 1);
            if (操作数1 > (int)全_标签列表 && 操作数1 < (int)全_标签列表 + 100000)
            {
                标签 = (struct label结构 *)操作数1;
                if (!标签->是否已定义)
                {
                    printf("错误: 未定义的标签 '%s'\n", 标签->名称);
                    exit(-1);
                }
                *(代码段1 + 1) = 标签->目标地址;
            }
        }
        代码段1++;
        if (代码段1 < l_全_代码段 && *代码段1 <= SJP) {
            代码段1++; // 跳过操作数
        }
    }

    // 回填未解析的函数引用
    函数修补 = 全_函数修补列表;
    while (函数修补) {
        if (!函数修补->函数标识符->是否已定义) {
            printf("错误: 函数 '%s' 已声明但未定义\n", 函数修补->函数标识符->字符串);
            exit(-1);
        }
        *(函数修补->地址) = 函数修补->函数标识符->地址;
        临时函数修补 = 函数修补;
        函数修补 = 函数修补->下个待修补引用点;
        free(临时函数修补);
    }
    全_函数修补列表 = ((void *)0);
}

// 解析函数参数列表
int 解析函数参数(struct 单词结构 *函数标识符) {
    int 参数偏移;

    参数偏移 = 0;

    取下个单词(); // 跳过 '('

    while (全_单词编码 != ')') {
        全_声明数据类型 = TYPE_INT;
        if (全_单词编码 == Int || 全_单词编码 == TOK_整数) {
            取下个单词();
        } else if (全_单词编码 == Int8 || 全_单词编码 == TOK_整数8) {
            取下个单词();
            全_声明数据类型 = TYPE_INT8;
        } else if (全_单词编码 == Int16 || 全_单词编码 == TOK_整数16) {
            取下个单词();
            全_声明数据类型 = TYPE_INT16;
        } else if (全_单词编码 == Int32 || 全_单词编码 == TOK_整数32) {
            取下个单词();
            全_声明数据类型 = TYPE_INT32;
        } else if (全_单词编码 == Int64 || 全_单词编码 == TOK_整数64) {
            取下个单词();
            全_声明数据类型 = TYPE_INT64;
        } else if (全_单词编码 == Char || 全_单词编码 == TOK_字符) {
            取下个单词();
            全_声明数据类型 = TYPE_CHAR;
        } else if (全_单词编码 == Struct || 全_单词编码 == TOK_结构体) {
            取下个单词();
            if (全_单词编码 != 标识类型_标识符) {
                printf("%d: 错误的结构体声明 \n", 全_行号);
                return -1;
            }
            全_声明数据类型 = 全_当前单词->表示的结构体名称;
            取下个单词();
        }

        while (全_单词编码 == 标点_星号) {
            取下个单词();
            全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
        }

        if (全_单词编码 != 标识类型_标识符) {
            printf("%d: 错误的参数声明 \n", 全_行号);
            return -1;
        }

        // 保存参数信息
        if (全_当前单词->声明类型 == 标识类型_局部) {
            printf("%d: 重复的参数定义 \n", 全_行号);
            return -1;
        }

        全_当前单词->类别备份 = 全_当前单词->声明类型;
        全_当前单词->声明类型 = 标识类型_局部;
        全_当前单词->数据类型备份 = 全_当前单词->数据类型;
        全_当前单词->数据类型 = 全_声明数据类型;
        全_当前单词->地址备份 = 全_当前单词->地址;
        全_当前单词->地址 = 参数偏移++;

        取下个单词();
        if (全_单词编码 == ',') {
            取下个单词();
        }
    }

    取下个单词(); // 跳过 ')'
    return 参数偏移;
}
// 输出项:数据类型
int 解析数据类型(int *数据类型)
{
    int 枚举成员值 , 结构体成员偏移;
    struct 结构体成员结构 *结构体成员列表; //指针被“全_结构体成员列表[*数据类型]”引用，释放时需谨慎。可以在释放“全_结构体成员列表”时一并释放掉“全_结构体成员列表[*数据类型]”
    int 当前标识符数据类型;

    if (全_单词编码 == Int || 全_单词编码 == TOK_整数)
        取下个单词();
    else if (全_单词编码 == Int8 || 全_单词编码 == TOK_整数8)
    {
        取下个单词();
        *数据类型 = TYPE_INT8;
    }
    else if (全_单词编码 == Int16 || 全_单词编码 == TOK_整数16)
    {
        取下个单词();
        *数据类型 = TYPE_INT16;
    }
    else if (全_单词编码 == Int32 || 全_单词编码 == TOK_整数32)
    {
        取下个单词();
        *数据类型 = TYPE_INT32;
    }else if (全_单词编码 == Int64 || 全_单词编码 == TOK_整数64)
    {
        取下个单词();
        *数据类型 = TYPE_INT64;
    }
    else if (全_单词编码 == Char || 全_单词编码 == TOK_字符)
    {
        取下个单词();
        *数据类型 = TYPE_CHAR;
    }
    else if (全_单词编码 == Enum || 全_单词编码 == TOK_枚举)
    {
        取下个单词();
        if (全_单词编码 != '{') 取下个单词();
        if (全_单词编码 == '{')
        {
            取下个单词();
            枚举成员值 = 0;
            while (全_单词编码 != '}')
            {
                if (全_单词编码 != 标识类型_标识符)
                {
                    printf("%d: 无效的枚举成员标识符 %d\n", 全_行号, 全_单词编码);
                    return -1;
                }
                取下个单词();
                if (全_单词编码 == 标点_赋值号)
                {
                    取下个单词();
                    if (全_单词编码 != 标识类型_数字)
                    {
                        printf("%d: 无效的枚举初始值\n", 全_行号);
                        return -1;
                    }
                    枚举成员值 = 全_单词的值;
                    取下个单词();
                }
                全_当前单词->声明类型 = 标识类型_数字;
                全_当前单词->数据类型 = TYPE_INT;
                全_当前单词->地址 = 枚举成员值++;
                if (全_单词编码 == ',')
                    取下个单词();
            }
            取下个单词();
        }
    }
    else if (全_单词编码 == Struct || 全_单词编码 == TOK_结构体)
    {
        取下个单词();
        if (全_单词编码 == 标识类型_标识符)
        {
            if (!全_当前单词->表示的结构体名称) 全_当前单词->表示的结构体名称 = 全_下个可用类型++;
            *数据类型 = 全_当前单词->表示的结构体名称;
            取下个单词();
        } else {
            *数据类型 = 全_下个可用类型++;
        }
        if (全_单词编码 == '{')
        {
            取下个单词();
            if (全_结构体成员列表[*数据类型])
            {
                printf("%d: 重复的结构体定义\n", 全_行号);
                return -1;
            }
            结构体成员偏移 = 0;
            while (全_单词编码 != '}')
            {
                当前标识符数据类型 = TYPE_INT;
                if (全_单词编码 == Int || 全_单词编码 == TOK_整数)
                    取下个单词();
                else if (全_单词编码 == Int8 || 全_单词编码 == TOK_整数8)
                {
                    取下个单词();
                    当前标识符数据类型 = TYPE_INT8;
                }else if (全_单词编码 == Int16 || 全_单词编码 == TOK_整数16)
                {
                    取下个单词();
                    当前标识符数据类型 = TYPE_INT16;
                }else if (全_单词编码 == Int32 || 全_单词编码 == TOK_整数32)
                {
                    取下个单词();
                    当前标识符数据类型 = TYPE_INT32;
                }else if (全_单词编码 == Int64 || 全_单词编码 == TOK_整数64)
                {
                    取下个单词();
                    当前标识符数据类型 = TYPE_INT64;
                }else if (全_单词编码 == Char || 全_单词编码 == TOK_字符)
                {
                    取下个单词();
                    当前标识符数据类型 = TYPE_CHAR;
                }else if (全_单词编码 == Struct || 全_单词编码 == TOK_结构体)
                {
                    取下个单词();
                    if (全_单词编码 != 标识类型_标识符)
                    {
                        printf("%d: 错误的结构体声明 \n", 全_行号);
                        return -1;
                    }
                    当前标识符数据类型 = 全_当前单词->表示的结构体名称;
                    取下个单词();
                }
                while (全_单词编码 != ';') // 成员定义
                {
                    全_声明数据类型 = 当前标识符数据类型;
                    while (全_单词编码 == 标点_星号)
                    {
                        取下个单词();
                        全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
                    }
                    if (全_单词编码 != 标识类型_标识符)
                    {
                        printf("%d: 无效的结构体成员定义 \n", 全_行号);
                        return -1;
                    }
                    结构体成员列表 = malloc(sizeof(struct 结构体成员结构));
                    结构体成员列表->成员单词 = 全_当前单词;
                    结构体成员列表->偏移量 = 结构体成员偏移;
                    结构体成员列表->数据类型 = 全_声明数据类型;
                    结构体成员列表->关联的结构体 = 全_结构体成员列表[*数据类型];
                    全_结构体成员列表[*数据类型] = 结构体成员列表;
                    结构体成员偏移 = 结构体成员偏移 + (全_声明数据类型 >= TYPE_PTR ? sizeof(int) : 全_数据类型大小[全_声明数据类型]);
                    结构体成员偏移 = (结构体成员偏移 + 3) & -4; //4字节对齐

                    取下个单词();
                    if (全_单词编码 == ',')
                        取下个单词();
                }
                取下个单词();
            }
            取下个单词();
            全_数据类型大小[*数据类型] = 结构体成员偏移;
        }
    }
    return 0;
}
void 解析声明类型(int 标识符类型)
{
    while (全_单词编码 == 标点_星号)
    {
        取下个单词();
        全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
    }
    if (全_单词编码 != 标识类型_标识符)
    {
        printf("%s第%d行: 错误的全局声明。\n", 全_源文件名称,全_行号);
        return -1;
    }
    取下个单词(); //跳过标识符
    if (全_单词编码 == 标点_左方括号)
    {
        取下个单词();
        if (全_单词编码 != ']')
        {
            if (全_单词编码 != 标识类型_数字)
            {
                printf("%d: 数组大小必须为数字\n", 全_行号);
                return -1;
            }else{
                全_声明数据类型 = 全_声明数据类型 + TYPE_ARRAY;
            }

        }else{
        }
    }

}

// 在 外部声明() 函数中，修改函数声明和定义的处理逻辑
int 外部声明()
{
    struct 单词结构 *当前单词,
            *当前标识符_备份;

    int            参数偏移 ,
            数据类型1,
            原类别,
            原数据类型,
            原地址,
            原是否已定义,
            原参数数量;

    while (全_单词编码)
    {
        数据类型1 = TYPE_INT; // 基本数据类型,默认为整数类型

        if (解析数据类型(&数据类型1) == -1)
        {
            return -1;
        }

        while (全_单词编码 != ';' && 全_单词编码 != '}') //此时token是数据类型后面的声明符
        {
            全_声明数据类型 = 数据类型1;

            while (全_单词编码 == 标点_星号)
            {
                取下个单词();
                全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
            }

            if (全_单词编码 != 标识类型_标识符)
            {
                printf("%s第%d行: 错误的全局声明。\n", 全_源文件名称,全_行号);
                return -1;
            }

            当前单词 = 全_当前单词;
            取下个单词();

            if (全_单词编码 == '(')
            {
                // 保存当前符号表状态，以便在参数解析后恢复
                当前标识符_备份 = 全_当前单词;

                // 保存函数标识符的原始状态（在解析参数前）
                原类别 = 当前单词->声明类型;
                原数据类型 = 当前单词->数据类型;
                原地址 = 当前单词->地址;
                原是否已定义 = 当前单词->是否已定义;
                原参数数量 = 当前单词->参数数量;

                // 解析参数列表
                参数偏移 = 解析函数参数(当前单词);
                if (参数偏移 == -1) {
                    return -1;
                }

                // 检查函数声明是否匹配
                if (检查函数声明(当前单词, 参数偏移, 全_声明数据类型) == -1)
                {
                    return -1;
                }

                // 如果是函数声明（以分号结尾）
                if (全_单词编码 == ';')
                {
                    // 检查是否重复声明
                    if (当前单词->声明类型 && 当前单词->声明类型 != 标识类型_函数) {
                        printf("%d: 重复的函数声明 '%s'\n", 全_行号, 当前单词->字符串);
                        return -1;
                    }

                    // 设置函数声明信息
                    当前单词->声明类型 = 标识类型_函数;
                    当前单词->数据类型 = 全_声明数据类型;
                    当前单词->参数数量 = 参数偏移;
                    // 对于声明，不设置 地址，等待定义时设置
                    // 保持 是否已定义 为 0

                    取下个单词(); // 跳过 ';'

                    // 完全恢复符号表状态
                    全_当前单词 = 全_单词表;
                    while (全_当前单词->单词编码)
                    {
                        if (全_当前单词->声明类型 == 标识类型_局部)
                        {
                            // 恢复局部变量（参数）的状态
                            全_当前单词->声明类型 = 全_当前单词->类别备份;
                            全_当前单词->数据类型 = 全_当前单词->数据类型备份;
                            全_当前单词->地址 = 全_当前单词->地址备份;
                        }
                        全_当前单词 = 全_当前单词 + 1;
                    }

                    // 恢复当前函数标识符的状态
                    当前单词->声明类型 = 原类别;
                    当前单词->数据类型 = 原数据类型;
                    当前单词->地址 = 原地址;
                    当前单词->是否已定义 = 原是否已定义;
                    当前单词->参数数量 = 原参数数量;



                    全_当前单词 = 当前标识符_备份;
                    goto 下一个声明;
                }else if (全_单词编码 == '{') // 导入函数（以 '{' 开头）
                {
                    if (当前单词->是否已定义)
                    {
                        printf("%d: 函数 '%s' 重复定义\n", 全_行号, 当前单词->字符串);
                        return -1;
                    }

                    当前单词->声明类型 = 标识类型_函数;
                    当前单词->数据类型 = 全_声明数据类型;
                    当前单词->地址 = (int)(全_代码段 + 1);
                    当前单词->参数数量 = 参数偏移;
                    当前单词->是否已定义 = 1;


                    全_局部变量偏移 = ++参数偏移; // 函数参数的偏移

                    // 局部变量
                    取下个单词(); // 跳过 '{'
                    while (全_单词编码 == Int || 全_单词编码 == TOK_整数||
                           全_单词编码 == Int8 || 全_单词编码 == TOK_整数8 ||
                           全_单词编码 == Int16 || 全_单词编码 == TOK_整数16 ||
                           全_单词编码 == Int32 || 全_单词编码 == TOK_整数32 ||
                           全_单词编码 == Int64 || 全_单词编码 == TOK_整数64 ||
                           全_单词编码 == Char || 全_单词编码 == TOK_字符 ||
                           全_单词编码 == Struct || 全_单词编码 == TOK_结构体)
                    {
                        if (全_单词编码 == Int || 全_单词编码 == TOK_整数)
                            数据类型1 = TYPE_INT;
                        else if (全_单词编码 == Int8 || 全_单词编码 ==TOK_整数8 )
                            数据类型1 = TYPE_INT8;
                        else if (全_单词编码 == Int16 || 全_单词编码 ==TOK_整数16 )
                            数据类型1 = TYPE_INT16;
                        else if (全_单词编码 == Int32 || 全_单词编码 ==TOK_整数32 )
                            数据类型1 = TYPE_INT32;
                        else if (全_单词编码 == Int64 || 全_单词编码 ==TOK_整数64 )
                            数据类型1 = TYPE_INT64;
                        else if (全_单词编码 == Char || 全_单词编码 == TOK_字符)
                            数据类型1 = TYPE_CHAR;
                        else
                        {
                            取下个单词();
                            if (全_单词编码 != 标识类型_标识符)
                            {
                                printf("%d: 错误的结构体声明 \n", 全_行号);
                                return -1;
                            }
                            数据类型1 = 全_当前单词->表示的结构体名称;
                        }
                        取下个单词();
                        while (全_单词编码 != ';')
                        {
                            全_声明数据类型 = 数据类型1;
                            while (全_单词编码 == 标点_星号)
                            {
                                取下个单词();
                                全_声明数据类型 = 全_声明数据类型 + TYPE_PTR;
                            }
                            if (全_单词编码 != 标识类型_标识符)
                            {
                                printf("%d: 错误的局部变量声明 \n", 全_行号);
                                return -1;
                            }
                            if (全_当前单词->声明类型 == 标识类型_局部)
                            {
                                printf("%d: 重复的局部变量定义 \n", 全_行号);
                                return -1;
                            }
                            全_当前单词->类别备份 = 全_当前单词->声明类型; // 以便后面能恢复标识符的类别
                            全_当前单词->声明类型 = 标识类型_局部;
                            全_当前单词->数据类型备份  = 全_当前单词->数据类型; // 以便后面能恢复标识符的数据类型
                            全_当前单词->数据类型 = 全_声明数据类型;
                            全_当前单词->地址备份   = 全_当前单词->地址;
                            全_当前单词->地址 = ++参数偏移;
                            取下个单词();
                            if (全_单词编码 == ',')
                                取下个单词();
                        }
                        取下个单词();
                    }

                    // 函数体(局部变量除外)
                    *++全_代码段 = ENT;  //进入子程序。这条指令会在运行时设置新的栈帧
                    *++全_代码段 = 参数偏移 - 全_局部变量偏移; //设置栈帧大小
                    while (全_单词编码 != '}') //循环解析函数体内的所有语句
                    {
                        解析语句();
                    }

                    *++全_代码段 = LEV; //离开子程序.这条指令会在运行时恢复调用前的栈帧

                    // 恢复符号表中的局部变量（参数和局部变量）
                    全_当前单词 = 全_单词表;
                    while (全_当前单词->单词编码)
                    {
                        if (全_当前单词->声明类型 == 标识类型_局部)
                        {
                            全_当前单词->声明类型 = 全_当前单词->类别备份;
                            全_当前单词->数据类型 = 全_当前单词->数据类型备份;
                            全_当前单词->地址 = 全_当前单词->地址备份;
                        }
                        全_当前单词 = 全_当前单词 + 1;
                    }

                    取下个单词(); // 跳过 '}'
                    goto 下一个声明;
                }
                else
                {
                    printf("%d: 函数声明后应为 ';' 或函数定义后应为 '{'\n", 全_行号);
                    return -1;
                }
            }
            else
            {
                // 全局变量
//                if (当前单词->声明类型)
//                {
//                    printf("%d: 重复的全局定义\n", 全_行号);
//                    return -1;
//                }
                当前单词->声明类型 = 标识类型_全局;
                当前单词->数据类型 = 全_声明数据类型;
                当前单词->地址 = (int)全_数据段;
                全_数据段 = 全_数据段 + sizeof(int);

                if (全_单词编码 == ',')
                    取下个单词();
            }
        } // while (全_单词编码 != ';' && 全_单词编码 != '}')
        下一个声明:
        取下个单词();
    } // while (全_单词编码)

    // 在所有声明解析完成后，回填goto标签地址
    修补labels();

    // 检查所有声明的函数是否都有定义
    全_当前单词 = 全_单词表;
    while (全_当前单词->单词编码)
    {
        if (全_当前单词->声明类型 == 标识类型_函数 &&
            !全_当前单词->是否已定义 &&
            全_当前单词->地址 == 0)
        {
            printf("%d :错误: 函数 '%s' 已声明但未定义\n",全_行号,全_当前单词->字符串);
            return -1;
        }
        全_当前单词 = 全_当前单词 + 1;
    }

    return 0;
}
int run()
{
    int 当前指令 , 指令统计 , 累加器 , *基址;

    指令统计 = 0;
    while (1)
    {
        当前指令 = *全_下条指令++;
        ++指令统计;
        if (全_打印指令)
        {
            printf("%d> %.4s", 指令统计,&全_操作码[当前指令 * 5]); //
            if (当前指令 <= SJP)
                printf(" %d\n", *全_下条指令);
            else
                printf("\n");
        }
        switch (当前指令)
        {
            case LLO: 累加器 = (int)(基址 + *全_下条指令++); break;                         // 加载局部变量地址：   lea rax, [rbp + 偏移量] ：48 8D 45 XX
            case LGL: 累加器 = *全_下条指令++; break;                                      // 加载全局地址或立即数：mov rax, 0xXXXXXXXX     ：48 C7 C0 XX XX XX XX
            case JMP: 全_下条指令 = (int *)*全_下条指令; break;                                // 无条件跳转：        jmp 0xXXXXXXXX          ：相对跳转：E9 XX XX XX XX    或   绝对跳转：FF 20 （jmp [rax]，假设地址在RAX中）
            case JSR: *--全_栈顶 = (int)(全_下条指令 + 1); 全_下条指令 = (int *)*全_下条指令; break;    // 跳转到子程序
            case ZJ:  全_下条指令 = 累加器 ? 全_下条指令 + 1 : (int *)*全_下条指令; break;          // 零跳转
            case NZJ: 全_下条指令 = 累加器 ? (int *)*全_下条指令 : 全_下条指令 + 1; break;          // 非零跳转
            case ENT: *--全_栈顶 = (int)基址; 基址 = 全_栈顶; 全_栈顶 = 全_栈顶 - *全_下条指令++; break;   // 进入子程序
            case SJP: 全_栈顶 = 全_栈顶 + *全_下条指令++; break;                                  // 栈调整
            case LEV: 全_栈顶 = 基址; 基址 = (int *)*全_栈顶++; 全_下条指令 = (int *)*全_栈顶++; break; // 退出子程序
            case LI:  累加器 = *(int *)累加器; break;                                 // 加载int
            case LI8:  累加器 = *(uint8_t *)累加器; break;                            // 加载uint8_t
            case LI16:  累加器 = *(uint16_t *)累加器; break;                            // 加载uint16_t
            case LI32:  累加器 = *(uint32_t *)累加器; break;                            // 加载uint32_t
            case LI64:  累加器 = *(uint64_t *)累加器; break;                            // 加载uint64_t
            case LC:  累加器 = *(char *)累加器; break;                                // 加载char
            case SI:  *(int *)*全_栈顶++ = 累加器; break;                                 // 存储 int
            case SI8:  *(uint8_t *)*全_栈顶++ = 累加器; break;                            // 存储uint8_t
            case SI16:  *(uint16_t *)*全_栈顶++ = 累加器; break;                            // 存储uint16_t
            case SI32:  *(uint32_t *)*全_栈顶++ = 累加器; break;                            // 存储uint32_t
            case SI64:  *(uint64_t *)*全_栈顶++ = 累加器; break;                            // 存储uint64_t
            case SC:  累加器 = *(char *)*全_栈顶++ = 累加器; break;                       // 存储char
            case PSH: *--全_栈顶 = 累加器; break;                                         // 压栈

            case OR:  累加器 = *全_栈顶++ |  累加器; break;
            case XOR: 累加器 = *全_栈顶++ ^  累加器; break;
            case AND: 累加器 = *全_栈顶++ &  累加器; break;
            case EQ:  累加器 = *全_栈顶++ == 累加器; break;
            case NE:  累加器 = *全_栈顶++ != 累加器; break;
            case LT:  累加器 = *全_栈顶++ <  累加器; break;
            case GT:  累加器 = *全_栈顶++ >  累加器; break;
            case LE:  累加器 = *全_栈顶++ <= 累加器; break;
            case GE:  累加器 = *全_栈顶++ >= 累加器; break;
            case SHL: 累加器 = *全_栈顶++ << 累加器; break;
            case SHR: 累加器 = *全_栈顶++ >> 累加器; break;
            case ADD: 累加器 = *全_栈顶++ +  累加器; break;
            case SUB: 累加器 = *全_栈顶++ -  累加器; break;
            case MUL: 累加器 = *全_栈顶++ *  累加器; break;
            case DIV: 累加器 = *全_栈顶++ /  累加器; break;
            case MOD: 累加器 = *全_栈顶++ %  累加器; break;

            case OPEN: 累加器 = open((char *)全_栈顶[2], 全_栈顶[1], *全_栈顶); break; //fd = open("hello.txt", 0x002 | 0x320 | 0x0400, 0644); // O_WRONLY | O_CREAT | O_TRUNC
            case READ: 累加器 = read(全_栈顶[2], (char *)全_栈顶[1], *全_栈顶); break;
            case CREA: 累加器 = creat((char *)全_栈顶[1], *全_栈顶); break;
            case WRIT: 累加器 = write(全_栈顶[2], (char *)全_栈顶[1], *全_栈顶); break; //int write(int fd, char *buf, int count);
            case LSEE: 累加器 = lseek(全_栈顶[2], 全_栈顶[1], *全_栈顶); break;
            case CLOS: 累加器 = close(*全_栈顶); break;
            case STRL: 累加器 = strlen((char *)*全_栈顶); break;
            case PRTF: 全_栈顶备份 = 全_栈顶 + 全_下条指令[1]; 累加器 = printf((char *)全_栈顶备份[-1], 全_栈顶备份[-2], 全_栈顶备份[-3], 全_栈顶备份[-4], 全_栈顶备份[-5], 全_栈顶备份[-6]); break;
            case MALC: 累加器 = (int)malloc(*全_栈顶); break;
            case MSET: 累加器 = (int)memset((char *)全_栈顶[2], 全_栈顶[1], *全_栈顶); break;
            case MCMP: 累加器 = memcmp((char *)全_栈顶[2], (char *)全_栈顶[1], *全_栈顶); break;
            case REAL: 累加器 = (int)realloc((void *)全_栈顶[1], *全_栈顶); break;
            case FREE: free((void *)*全_栈顶); 累加器 = 0; break;
            case MCPY: 累加器 = (int)memcpy((void *)全_栈顶[2], (void *)全_栈顶[1], (int)*全_栈顶); break;
            case SCPY: 累加器 = (int)strcpy((char *)全_栈顶[1], (char *)*全_栈顶); break;  // strcpy(dest, src)
            case SNCP: 累加器 = (int)strncpy((char *)全_栈顶[2], (char *)全_栈顶[1], (int)*全_栈顶);break; // strncpy(dest, src, n)
            case SCMP: 累加器 = strcmp((char *)全_栈顶[1], (char *)*全_栈顶); break;     // strcmp(s1, s2)
            case SNCM: 累加器 = strncmp((char *)全_栈顶[2], (char *)全_栈顶[1], (int)*全_栈顶); break;
            case SCAT: 累加器 = (int)strcat((char *)全_栈顶[1], (char *)*全_栈顶); break;
            case SDUP: 累加器 = (int)strdup((char *)*全_栈顶); break;  // 添加 strdup 支持
            case EXIT: printf("退出(%d) 指令统计 = %d\n", *全_栈顶, 指令统计); return *全_栈顶;
            default: printf("未知指令 = %d! 指令统计 = %d\n", 当前指令, 指令统计); return -1;
        }
    }
    return 0;
}

void 调试()
{
    // 在外部声明()函数结束后添加
    printf("=== 编译完成统计 ===\n");
    printf("代码段大小: %ld 字节\n", (char*)全_代码段 - (char*)起始_全_代码段);
    printf("数据段大小: %ld 字节\n", (char*)全_数据段 - (char*)起始_全_数据段);
    printf("符号表使用: %ld 个标识符\n", ((char*)全_当前单词 - (char*)全_单词表) / sizeof(struct 单词结构));

// 打印main函数信息
    printf("main函数地址: %p\n", (void*)单词main->地址);
    printf("'开始'函数地址: %p\n", (void*)单词开始->地址);

// 检查函数是否已定义
    if (单词main->是否已定义) {
        printf("main函数已定义，参数数量: %d, 返回类型: %d\n",
               单词main->参数数量, 单词main->数据类型);
    } else {
        printf("main函数未定义\n");
    }

    if (单词开始->是否已定义) {
        printf("'开始'函数已定义，参数数量: %d, 返回类型: %d\n",
               单词开始->参数数量, 单词开始->数据类型);
    } else {
        printf("'开始'函数未定义\n");
    }
}
int 开始(int 参数量, char **参数组)
{
    int 文件描述 ,  关键词编码 , 库函数编码 , 文件字节数;

    --参数量; ++参数组;
    if (参数量 > 0 && **参数组 == '-' && (*参数组)[1] == 's') { 全_打印源码 = 1; --参数量; ++参数组; }
    if (参数量 > 0 && **参数组 == '-' && (*参数组)[1] == 'd') { 全_打印指令 = 1; --参数量; ++参数组; }
    if (参数量 < 1) { printf("用法: zhie [-s] [-d] 输入文件 ...\n"); return -1; }
//0只读；1只写；2可读可写
    if ((文件描述 = open(*参数组, 0)) < 0) { printf("无法打开文件(%s)\n", *参数组); return -1; }
    全_源文件名称 = *参数组;

    源码缓冲区大小 = 512*1024;
    标识符最大长度 = 512;
    全_标签列表 = ((void *)0);
    全_函数修补列表 = ((void *)0);

    if (!(全_单词表 = malloc(源码缓冲区大小))) { printf("无法为符号表分配 (%d) 字节的内存(malloc)\n", 源码缓冲区大小); return -1; }
    if (!(起始_全_代码段 = l_全_代码段 = 全_代码段 = malloc(源码缓冲区大小))) { printf("无法为代码段分配(%d) 字节的内存(malloc)\n", 源码缓冲区大小); return -1; }
    if (!(起始_全_数据段 = 全_数据段 = malloc(源码缓冲区大小))) { printf("无法为数据段分配(%d) 字节的内存(malloc)\n", 源码缓冲区大小); return -1; }
    if (!(全_栈顶 = malloc(源码缓冲区大小))) { printf("无法为栈分配(%d)字节的内存(malloc)\n", 源码缓冲区大小); return -1; }
    if (!(全_数据类型大小 = malloc(TYPE_PTR * sizeof(int)))) { printf("无法为数据类型大小数组分配内存(malloc)\n"); return -1; }
    if (!(全_结构体成员列表 = malloc(TYPE_PTR * sizeof(struct 结构体成员结构 *)))) { printf("无法为结构体成员的列表分配内存(malloc)\n"); return -1; }

    memset(全_单词表,  0, 源码缓冲区大小);
    memset(全_代码段,    0, 源码缓冲区大小);
    memset(全_数据段, 0, 源码缓冲区大小);
    memset(全_数据类型大小,   0, TYPE_PTR * sizeof(int));
    memset(全_结构体成员列表, 0, TYPE_PTR * sizeof(struct 结构体成员结构 *));


    全_操作码 = "LLO  LGL  JMP  JSR  ZJ   NZJ  ENT  SJP  LEV  LI   LI8  LI16  LI32 LI64  LC   SI   SI8  SI16  SI32  SI64  SC   PSH  "
                "OR   XOR  AND  EQ   NE   LT   GT   LE   GE   SHL  SHR  ADD  SUB  MUL  DIV  MOD  "
                "OPEN READ CREA WRIT LSEE CLOS STRL PRTF MALC MSET MCMP REAL FREE MCPY SCPY SNCP SCMP SNCM SCAT SDUP EXIT ";

    全_源码指针 = "break 跳出 case 分支 char 字符 uint8_t 整数8 uint16_t 整数16 uint32_t 整数32 uint64_t 整数64 default 默认 else 否则 "
                  "enum 枚举 if 如果 int 整数 return 返回 sizeof 取大小 struct 结构体 switch 选择 goto 跳转 判断 while "
                  "open read creat write lseek close strlen printf malloc memset memcmp realloc free memcpy strcpy strncpy "
                  "strcmp strncmp strcat strdup exit void 无 main 开始";
    关键词编码 = Break;
    while (关键词编码 <= While)
    {
        取下个单词();
        全_当前单词->单词编码 = 关键词编码++;
    } // 添加关键词到符号表

    库函数编码 = OPEN;
    while (库函数编码 <= EXIT)
    {
        取下个单词();
        全_当前单词->声明类型 = 标识类型_系统;
        全_当前单词->数据类型 = TYPE_INT;
        全_当前单词->地址 = 库函数编码++;
        全_当前单词->是否已定义 = 1; // 系统函数视为已定义

    } // 添加库函数到符号表
    取下个单词();
    全_当前单词->单词编码 = Char; // 添加void到符号表

    取下个单词();
    全_当前单词->单词编码 = Char; // 添加"无"到符号表

    取下个单词();
    单词main = 全_当前单词;    // 设置main函数在符号表位置，在解析函数的时候，main的地址会被修为在代码段的位置。

    取下个单词();
    单词开始 = 全_当前单词;    // 设置开始函数在符号表位置，在解析函数的时候，“开始”的地址会被修为在代码段的位置。

    if (!(l_全_源码指针 = 全_源码指针 = malloc(源码缓冲区大小)))
    {
        printf("无法为源区域分配(%d)字节的内存\n", 源码缓冲区大小);
        return -1;
    }
    //此时“文件字节数”的值是实际读取的字节数
    if ((文件字节数 = read(文件描述, 全_源码指针, 源码缓冲区大小-1)) <= 0)
    {
        printf("read() 返回的字节数是 %d\n", 文件字节数);
        return -1;
    }
    全_源码指针[文件字节数] = 0; // 添加字符串结束符
    close(文件描述);

    // 数组索引按顺序对应 "enum 数据类型" 中的 CHAR, INT8, INT 等类型标识符。
    全_下个可用类型 = 0;
    全_数据类型大小[全_下个可用类型++] = sizeof(char);
    全_数据类型大小[全_下个可用类型++] = 1; // uint8_t
    全_数据类型大小[全_下个可用类型++] = 2; // uint16_t
    全_数据类型大小[全_下个可用类型++] = 4; // uint32_t
    全_数据类型大小[全_下个可用类型++] = 8; // uint64_t
    全_数据类型大小[全_下个可用类型++] = sizeof(int);

    // 开始解析声明
    全_行号 = 1;
    取下个单词();
    if (外部声明() == -1) { //语法分析
        return -1;
    }

    调试();
    if (!(全_下条指令 = (int *)单词main->地址) && !(全_下条指令 = (int *)单词开始->地址))
    {
        printf("开始()/main() 函数未定义\n");
        return -1;
    }
    if (全_打印源码)
        return 0;

    // 设置栈
    全_栈顶 = (int *)((int)全_栈顶 + 源码缓冲区大小); //将栈顶指针移动到分配内存的末尾，建立栈空间的上界
    *--全_栈顶 = EXIT;                      // 在栈顶压入 EXIT 指令，确保当 main 函数返回时能够调用 exit 系统调用退出程序
    *--全_栈顶 = PSH;                       //压入 PSH（push）指令，用于后续参数传递
    全_栈顶备份 = 全_栈顶;                   // 保存当前栈顶位置的临时备份
    *--全_栈顶 = 参数量;                     //压入命令行参数的数量
    *--全_栈顶 = (int)参数组;                //压入命令行参数数组的地址
    *--全_栈顶 = (int)全_栈顶备份;           //压入之前保存的栈顶位置，形成完整的调用栈结构

    run();                               /* 在虚拟环境运行 */
    return 0;
}

// 检查函数声明是否匹配
int 检查函数声明(struct 单词结构 *函数标识符, int 参数数量, int 返回类型)
{
    // 如果函数已经定义过，检查是否匹配
    if (函数标识符->是否已定义) {
        if (函数标识符->参数数量 != 参数数量) {
            printf("%d: 函数 '%s' 参数数量不匹配\n", 全_行号, 函数标识符->字符串);
            return -1;
        }
        if (函数标识符->数据类型 != 返回类型) {
            printf("%d: 函数 '%s' 返回类型不匹配\n", 全_行号, 函数标识符->字符串);
            return -1;
        }
    }
    return 0;
}